package com.aijk.commons.scan;

import com.aijk.commons.exception.ErrorCodeException;
import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Created by iscdw on 2016/4/10.
 */
public class AnnotationScan {
    private static final Logger logger = LoggerFactory.getLogger(AnnotationScan.class);

    private AnnotationScan() {

    }

    /**
     * 扫描注解并获得类,并进行进行处理
     *
     * @param scanRootPackage 扫描包路径
     * @param annotation      注解类
     * @param handler         扫描结果处理器
     */
    public static void autoLoadByAnnotation(String scanRootPackage, Class<? extends Annotation> annotation, AnnotationProcessHandler handler) {
        if (scanRootPackage == null || scanRootPackage.length() == 0) {
            return;
        }

        Reflections reflections = new Reflections(scanRootPackage);
        Set<Class<?>> sets = reflections.getTypesAnnotatedWith(annotation);

        for (Class<?> clazz : sets) {
            handler.process(clazz);
        }
    }

    /**
     * 扫描注解并获得ScanResult对象,进行注册,key不可重复
     *
     * @param scanRootPackage 扫描包路径
     * @param annotation      注解类
     * @param handler         扫描结果处理器
     */
    public static void autoLoadByAnnotation(String scanRootPackage, Class<? extends Annotation> annotation, AnnotationClassHandler handler) {
        if (scanRootPackage == null || scanRootPackage.length() == 0) {
            return;
        }

        Reflections reflections = new Reflections(scanRootPackage);
        Set<Class<?>> sets = reflections.getTypesAnnotatedWith(annotation);
        Map<String, ScanResult> map = new HashMap<>();
        for (Class<?> clazz : sets) {
            ScanResult result = handler.process(clazz);
            if (result != null && result.getKey() != null && result.getKey().trim().length() > 0) {
                if (map.containsKey(result.getKey())) {
                    logger.error("扫描类注解重复键值,key={},class={},annotation={}", new Object[]{result.getKey(), result.getClazz(), annotation});
                } else {
                    map.put(result.getKey(), result);
                    handler.register(result);
                }
            }
        }
    }

    /**
     * 扫描注解的方法并返回SacnResult对象进行注册,key不可重复
     *
     * @param scanRootPackage 扫描包路径
     * @param annotation      注解类
     * @param handler         扫描结果处理器
     */
    public static void autoLoadMethodByAnnotation(String scanRootPackage, Class<? extends Annotation> annotation, AnnotationMethodHandler handler) {
        if (scanRootPackage == null || scanRootPackage.length() == 0) {
            return;
        }

        Map<String, ScanResult> map = new HashMap<>();

        Reflections reflections = new Reflections(scanRootPackage, new MethodAnnotationsScanner());
        Set<Method> sets = reflections.getMethodsAnnotatedWith(annotation);

        for (Method method : sets) {
            ScanResult result = handler.process(method);
            if (result != null && result.getKey() != null && result.getKey().trim().length() > 0) {
                if (map.containsKey(result.getKey())) {
                    logger.error("扫描方法注解重复键值,key={},class={},annotation={}", new Object[]{result.getKey(), result.getClazz(), annotation});
                    continue;
                }

                if (result.getClazz() == null) {
                    result.setClazz(method.getDeclaringClass());
                }
                if (result.getMethod() == null) {
                    result.setMethod(method);
                }
                if (result.getReturnType() == null) {
                    result.setReturnType(method.getReturnType());
                }
                if (result.getParameterTypes() == null) {
                    result.setParameterTypes(method.getParameterTypes());
                }
                map.put(result.getKey(), result);
                handler.register(result);
                logger.info("注册方法键值,key={},class={},annotation={}", new Object[]{result.getKey(), result.getClazz(), annotation});

            } else if (result != null && StringUtils.isEmpty(result.getKey())) {
                throw new ErrorCodeException("ScanResult.key不能为空");
            }
        }
    }
}
